﻿/*Conditional Compilation */
#define DEBUG

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

//Using serial port namespace
using System.IO.Ports;

// CancelEventArgs
using System.ComponentModel;

/**
 * Make sure that you have added references to Visifire assembly, 
 * WPFVisifire.Charts.dll. Do not forget to include the Visifire.Charts 
 * and Visifire.Commons within any code file where you want to work 
 * with the charts. These files can be added with following syntax:
 * */
using Visifire.Charts;
using Visifire.Commons;

using GasData;

/*For observable collection */
using System.Collections.ObjectModel;

/* Threading */
/* Synchronized methods */
using System.Threading;
using System.Runtime.CompilerServices;

using WebRequestExample;

/* Ntp client to get UTC time */
using MFToolkit.Net.Ntp;

using System.IO;

namespace WPF_DataLogger
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();         
            UserComponentsInit();
        }

        /*****************************************************
         * Function:
         * Summary:
         * Input:
         * Returns:
         * Side effects:
         * Pre-Conditions:
        ******************************************************/
        private void UserComponentsInit()
        {
            /*Construct a new serial port*/
            sp1 = new SerialPort();

            //_gas_data_table = new Dictionary<String, GasInfo>();
            _gas_data_table = new GasTable();

            /* Asynchronous read */
            //sp1_read_head_callback = new AsyncCallback(ReadComplete);

            //sp1_frame_err_callback = new AsyncCallback(FrameErrReadComplete);

            /* Register the next log event */
            LogNextPack += new LogNextPacketHandler(LogDataBtn_Click);

            /* Regsiter the read gas pack event */
            //ReadGasPack += new GetPackGasPart(ReadGasPayload);

            SendDataToServerEve += new SendDataToServer(SendToWebBtn_Click);


            /* Do a data binding for the textProperty of the connection status 
               console
             */
            COMStatus_text = new TextBoxContent("No Connection");

            COMStatus.DataContext = COMStatus_text;

            //By default, we assume that the user wishes to continuously
            //log data;
            continue_to_log = true;

            g_frame_error = false; 

            decoder_type = DecoderType.BlueTooth;
            BluetoothDecoder.IsChecked = true;
            MRFDecoder.IsChecked = false;

            /* Sensor Family */
            _sensor_family = SensorFamily.MQ;
            MQFamily.IsChecked = true;

            /*Initialize gas name list*/
            gas_name_list = new List<string>(); 

            gas_name_list.Add("CO");
            gas_name_list.Add("NO2");
            gas_name_list.Add("O3");
            //gas_name_list.Add("PM10");
            //gas_name_list.Add("SO2");

            /*Aqi*/
            try
            {
                _aqi_calc = new AQICalculator();
                AQIStandard.IsChecked = true;
                _aqi_calc.CreateLookupTable();
            }
            catch (FileNotFoundException ex)
            {
                System.Console.WriteLine(ex.Message);
            }

            /* Set up web post */
            //myPostToWeb = new WebPostRequest("http://www.simplesite.com/airQuality/",0);
            //myPostToWeb.GetResponse("GET");

            /* Auto send to server feature */
            _auto_send_server = true;
            AutoSendToServer.IsChecked = true;

            /* Data file logging */
            if (!System.IO.File.Exists(gasLogFileName))
            {
                System.IO.File.Create(gasLogFileName);
            }

            LoadLogSensorFile();

            LoadIniConfigTable();

            InitGPS();
        }

        private void LoadIniConfigTable(){
            iniConfigTable = new Dictionary<string, string>();

            if(System.IO.File.Exists(configFileName)){

                using (StreamReader iniConfigReader = new StreamReader(configFileName))
                {
                    while (iniConfigReader.Peek() > 0)
                    {
                        string command = iniConfigReader.ReadLine();
                        string [] commands = command.Split('=');
                        iniConfigTable.Add(commands[0], commands[1]);
                    }
                    iniConfigReader.Close();
                }

            }
        }

        private void InitGPS(){
            try
            {
                using (StreamReader iniConfigReader = new StreamReader(configFileName))
                {
                    _longitude = iniConfigTable["longitude"];
                    _latitude = iniConfigTable["latitude"];
                }
            }
            catch (IOException)
            {
                //Use arbitrary values
                _longitude = "103.772929";
                _latitude = "1.294571";
            }
        }

        private void LoadLogSensorFile(){
            FileInfo fileInfo = new FileInfo(gasLogFileName);

            if (fileInfo.Exists)
            {
                if (fileInfo.Length <= 0)
                {
                    WriteGasLogHeader();
                }
                
            }
            else
            {
                System.IO.File.Create(gasLogFileName);
                if (fileInfo.Length <= 0)
                {
                    WriteGasLogHeader();
                }
            }
        }

        //Write the types of gases in the 1st line of the file;
        //Pre _log_fs_writer is opened as to write
        private void WriteGasLogHeader()
        {
            try
            {
                using (StreamWriter writer = new StreamWriter(gasLogFileName, true))
                {
                    foreach (string name in gas_name_list)
                    {
                        string data = name + " ";
                        writer.Write(data);
                    }
                    writer.Write(Environment.NewLine);
                }
            }
            catch (IOException ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /* Hardware clean up is very important for system
         * reusability. So before the main window is closed,
         * clean up hardware and memory.
         */
        /*****************************************************
         * Function:
         * Summary:
         * Input:
         * Returns:
         * Side effects:
         * Pre-Conditions: This method is registered in XAML file
        ******************************************************/
        public void Window1_Closing(object sender, CancelEventArgs e)
        {
            //Close serial port;
            while (sp1.IsOpen)
            {
                sp1.Close();
            }

            //MessageBox.Show(sp1.PortName + " closed.");
            sp1.Dispose();

            /*Cleanup gas table*/
            _gas_data_table.Clear();

            /* Clean up gas_name_list */
            gas_name_list.Clear();

            /* Clear web post memory stuff */
            //myPostToWeb.ClearLocalData();

            _aqi_calc.Cleanup();

            //Write init file
            WriteInitConfigFile();

            //Then clear the configuration table.
            iniConfigTable.Clear();
        }

        private void WriteInitConfigFile()
        {
           try{
               System.IO.File.Delete(configFileName);
               using (StreamWriter writer = new StreamWriter(configFileName, true))
               {
                   foreach (KeyValuePair<string, string> configPair in iniConfigTable)
                   {
                       writer.WriteLine(configPair.Key +"="+configPair.Value);
                   }
               }
           }catch(IOException ex){
               System.Console.WriteLine(ex.Message);
           }
           
        }

        private void ExitBtn_Click(object sender, RoutedEventArgs e)
        {
            /*First clean up*/

            /* Equivalent to Application.Exit() in Windows Form */
            Application.Current.Shutdown();
        }

        private void ShowDataBtn_Click(object sender, RoutedEventArgs e)
        {
            /*Data Present*/
            newDataPresentation = null;
            newDataPresentation = new DataPresent();

            /* Register a listener from the dataPresent window, 
               i.e. the data present subscribes to this event.
             */
            //GasDataUpdated += new GasDataUpdateHandler(newDataPresentation.GasDataReceiving);
            GasDataUpdated += newDataPresentation.GasDataReceiving;

            /* Main window subscribes to DataPresent window close event ,
               which will re-enable the show data button
             */
            newDataPresentation.Closed += new EventHandler(EnableShowDataBtn);
            newDataPresentation.Show();

            ShowDataBtn.IsEnabled = false;

            /* Now check whether we should do the initial update of our graph*/
            GasDataUpdateEventArgs gas_params = null;
            gas_params = new GasDataUpdateEventArgs(_gas_data_table);

            if (newDataPresentation != null)
            {
                OnGasDataUpdated(gas_params);
            }

            gas_params = null;

        }

        public void EnableShowDataBtn(object sender, EventArgs cArgs)
        {
            GasDataUpdated -= newDataPresentation.GasDataReceiving;
            ShowDataBtn.IsEnabled = true;
        }

        private void ConSerialBtn_Click(object sender, RoutedEventArgs e)
        {

            if (!sp1.IsOpen)
            {
                sp1.PortName = this.SelCOM.Text;
                sp1.DataBits = 8;
                sp1.Parity = Parity.None;
                sp1.StopBits = StopBits.One;
                sp1.ReadTimeout = 1000;
                sp1.ReadBufferSize = 65536;

                switch(decoder_type)
                {
                    case DecoderType.BlueTooth:
                    sp1.BaudRate = 115200;
                    break;

                    case DecoderType.MRF:
                    default:
                    sp1.BaudRate = 9600;
                    break;
                }
            }

            try
            {
                sp1.Open();
            }
            catch (System.Exception exception)
            {
                LogTerminalInfo(exception.Message);
                sp1.Dispose();
                return;
            }

            ClearLogBtn_Click(null,null);
            LogTerminalInfo(sp1.PortName + " " + "connected.");

            connStatLabel.Background = Brushes.GreenYellow;
            connStatLabel.Content = "CONNECTED";

            continue_to_log = true;

            /* Now updata various form components' status*/
            UpdateWindow1ComponentsStat();         
        }

        /*****************************************************
         * Function:
         * Summary:
         * Input:
         * Returns:
         * Side effects:
         * Pre-Conditions:
        ******************************************************/
        void UpdateWindow1ComponentsStat()
        {
            LogDataBtn.IsEnabled = true;
        }

        private void RunSP1Read()
        {
            int numBytesRead = 0;
            PackDecoder decoder = SelectDecoder(null, decoder_type);

            while (continue_to_log)
            {
                try
                {
                    LogTerminalInfo("Receving data ...");
                    numBytesRead += sp1.Read(packet, numBytesRead, decoder.HeaderLength);
                    LogTerminalInfo("Num bytes read " + numBytesRead);
                    if (numBytesRead < decoder.HeaderLength) continue;

                    //Now enough bytes are read;

                    //Copy packets for analysis
                    decoder.CopyPacket(packet);

                    //Is header correct?
                    if (!decoder.IsHeaderCorrect)
                    {
                        LogTerminalInfo("Packet header corrupted. Discarding...");
                        //discard the rest of the packet
                        byte[] garbage = new byte[sp1.BytesToRead];
                        while (sp1.BytesToRead > 0)
                        {
                            sp1.Read(garbage, 0, sp1.BytesToRead);
                            LogTerminalInfo("Num bytes read " + numBytesRead);
                        }

                        SendNAK(sp1);
                        //Then continue to log the next packet;
                        numBytesRead = 0;
                        continue;
                    }

                    //Next process the payload

                    //First read up to enough bytes
                    int readBytes = decoder.HeaderLength + decoder.PackPayloadLen + decoder.PacketTailLength;
                    while (numBytesRead < readBytes)
                    {
                        numBytesRead += sp1.Read(packet, numBytesRead, readBytes - numBytesRead);
                        decoder.CopyPacket(packet);
                    }

                    if (!decoder.IsTailCorrect)
                    {
                        LogTerminalInfo("Packet corrupted. Discarding...");

                        //Clear the rest of buffer;
                        while (sp1.BytesToRead > 0)
                        {
                            byte[] garbage = new byte[packet.Length];
                            sp1.Read(garbage, 0, sp1.BytesToRead);
                        }

                        //Read Next packet;
                        SendNAK(sp1);
                        numBytesRead = 0;
                        continue;
                    }

                    LogTerminalInfo("Sensor Installed:" + " " + decoder.GetGasNum());

                    try
                    {
                        WriteToTable(this, packet);
                    }
                    catch (System.NullReferenceException ex)
                    {
                        MessageBox.Show(ex.Message, "ReadGasPayload Exception", MessageBoxButton.OK);
                    }

                    if (_auto_send_server)
                    {

                        Action action_take = new Action(
                        delegate()
                        {
                            OnSendDataToServer(null);
                        });

                        Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);

                    }

                    LogTerminalInfo("Reading process is completed. ");
                    //First ACK this packet
                    SendACK(sp1);

#if (DEBUG)
                    LogTerminalInfo("Send ACK");
#endif
                    numBytesRead = 0;

                }
                catch (TimeoutException ex)
                {
                    System.Console.WriteLine(ex.Message);
                    continue;
                    //LogTerminalInfo(ex.Message);
                }
                catch (InvalidOperationException ex)
                {
                    //I.e. the port is closed
                    LogTerminalInfo(ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    LogTerminalInfo(ex.Message);
                    break;
                }
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        private void LogDataBtn_Click(object sender, RoutedEventArgs e)
        {
            //IAsyncResult sp1_result;
            cur_read_start = false;
            enable_gas_read = false;
            PackDecoder decoder = SelectDecoder(null,decoder_type);

            LogDataBtn.IsEnabled = false;

            if (packet == null)
            {
                packet = new byte[BUF_SIZE];
            }

            continue_to_log = true;
            Thread testThread = new Thread(RunSP1Read);
            testThread.Start();
 
            return;

            /*
            if (continue_to_log)
            {
                try
                {
                    //First read header
                    if (!cur_read_start && !enable_gas_read)
                    {
                        //LogTerminalInfo("decoder header length: " + decoder.HeaderLength);
                     
                        sp1_result = sp1.BaseStream.BeginRead(packet, 0, decoder.HeaderLength, sp1_read_head_callback, sp1);

                        if (g_frame_error)
                        {
                            return;
                        }

                        cur_read_start = true;

                        LogTerminalInfo("Receiving data...");
                    }

                }
                catch (System.Exception ex)
                {
                    LogTerminalInfo(ex.Message);
                    cur_read_start = false;
                }
            }
            else if (g_frame_error)
            {
                LogTerminalInfo( "Resolving frame error ...");
                sp1_result = sp1.BaseStream.BeginRead(packet, 0, 1, sp1_frame_err_callback, sp1);
            }
            */
        }

        private void StopLogBtn_Click(object sender, RoutedEventArgs e)
        {
            //continue_to_log = true;
            g_frame_error = false;

            continue_to_log = false;
            LogDataBtn.IsEnabled = true;

            connStatLabel.Background = Brushes.White;
            connStatLabel.Content = "NO CONNECTION";

            /*
            if (sp1.IsOpen)
            {
                try
                {
                    sp1.Close();
                    LogDataBtn.IsEnabled = true;
                }
                catch (System.Exception ex)
                {
                    LogTerminalInfo(ex.Message);
                }
            }
            */

            LogTerminalInfo("Log stops");
        }

        private void ClearLogBtn_Click(object sender, RoutedEventArgs e)
        {
            COMStatus_text.Text_content = "";
        }

        /*****************************************************
         * Function:
         * Summary:
         * Input:
         * Returns:
         * Side effects:
         * Pre-Conditions:
        ******************************************************/
        private volatile bool cur_read_start;
        private byte[] packet;
        private volatile bool continue_to_log;
        private bool enable_gas_read = false;
        private volatile bool g_frame_error;   

        
        /*
        /// <summary>
        /// The asynchronous read complete function
        /// </summary>
        /// <param name="result"></param>
        protected void ReadComplete(IAsyncResult result)
        {
            int bytes_read;
            RoutedEventArgs e = new RoutedEventArgs();
            PackDecoder decoder = SelectDecoder(packet, decoder_type);

            // It is important to end the asyn read properly 
            //  One BeginRead must be followed by one EndRead
             

            try
            {
                bytes_read = 0;
                bytes_read = sp1.BaseStream.EndRead(result);

                LogTerminalInfo("bytes read: " + bytes_read);

                //This is to accomodate bluetooth devices;
                //If no bytes received, log next pack;
                if (decoder_type == DecoderType.BlueTooth && bytes_read == 0)
                {
                    LogTerminalInfo("ReadComplete sleep");

                    Action action_take = new Action(
                        delegate()
                        {
                            OnLogNextPack(e);
                        });

                    Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);
                }

                // If not enough bytes are read, continue 
                while (bytes_read < decoder.HeaderLength)
                {
                    bytes_read += sp1.Read(packet, bytes_read, decoder.HeaderLength - bytes_read);
                }

                // Check whether the header is valid 

                // first reconstruct the packet
                decoder.CopyPacket(packet);

                if (!decoder.IsHeaderCorrect)
                {
                    //Now the header is  not correct
                    byte [] garbage;
                    continue_to_log = true;
                    g_frame_error = false;

#if (DEBUG)
                    for (int idx = 0; idx < decoder.HeaderLength; idx ++ )
                    {
                        LogTerminalInfo("Header " + idx + " " + packet[idx]);
                    }
#endif
                    LogTerminalInfo("Header: Frame error occurs");

#if (DEBUG)
                    LogTerminalInfo("Bytes to read: " + sp1.BytesToRead);
#endif

                    garbage = new byte [sp1.BytesToRead];

                    //Discard the rest of the packet
                    while (sp1.BytesToRead > 0)
                    {
                        int num = sp1.Read(garbage, 0, sp1.BytesToRead);
                        LogTerminalInfo("Remaining bytes read: " + num);
                    }


                    //Send NAK information back to the transmitter
                    SendNAK(sp1);
#if (DEBUG)
                    LogTerminalInfo("Send NAK");
#endif

                    Action action_take = new Action(
                        delegate()
                        {
                            OnLogNextPack(e); 
                        });

                    Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);
                    
                    return;
                }

                // Fire the read gas payload event
                //OnReadGasPack(e);
                enable_gas_read = true;
                LogTerminalInfo("Reading sensor values...");
                ReadGasPayload(this, e);

            }
            catch (System.InvalidOperationException)
            {
                if (sp1.IsOpen)
                {
                    sp1.Close();
                    LogTerminalInfo(sp1.PortName + " " + "closed.");
                }
            }
            catch(System.Exception ex)
            {
                LogTerminalInfo(ex.Message);
            }
        }
        */

        /*
        //The process of resolving frame error
        protected void FrameErrReadComplete(IAsyncResult result)
        {
            int bytes_read;
            RoutedEventArgs e = new RoutedEventArgs();
            PackDecoder decoder = SelectDecoder(packet, decoder_type);

            // It is important to end the asyn read properly 
            //  One BeginRead must be followed by one EndRead
            
            try
            {
                bytes_read = sp1.BaseStream.EndRead(result);

                LogTerminalInfo("E, bytes read: " + bytes_read);

                // If not enough bytes are read, continue
                if (bytes_read < 1)
                {
                    sp1.Read(packet, 0, 1);
                }

                decoder.CopyPacket(packet);

                //Check Preambles
                for (int idx = 0; idx < decoder.PreambleLength - 1; idx ++){
                    
                    if (!decoder.IsPreambleData(packet[idx]))
                    {
                       LogTerminalInfo("Not preamble data.");
                       continue_to_log = false;
                       g_frame_error = true;
                      
                       //Send yet another NAK message
                       SendNAK(sp1);
#if (DEBUG)
                       LogTerminalInfo("Send NAK");
#endif

                       //TODO: call another log event;
                       OnLogNextPack(e);
                       return;
                    }

                    sp1.Read(packet, idx + 1, 1);
                }


                //Check the last byte of preamble
                if (!decoder.IsPreambleEnd(packet[decoder.PreambleLength - 1]))
                {
                    LogTerminalInfo("Not preamble end.");
                    continue_to_log = false;
                    g_frame_error = true;

                    //Send yet another NAK message
                    SendNAK(sp1);

#if (DEBUG)
                    LogTerminalInfo("Send NAK");
#endif

                    //TODO: call another log event;
                    OnLogNextPack(e);
                    return;
                }

                // All test passed and now resume the reading
                continue_to_log = true;
                g_frame_error = false;

                //Send the acknowledgement message
                SendACK(sp1);

#if (DEBUG)
                LogTerminalInfo("Send ACK");
#endif
                // Fire the read gas payload event
                //OnReadGasPack(e);
                enable_gas_read = true;
                LogTerminalInfo("Reading sensor values...");
                ReadGasPayload(this, e);

            }
            catch (System.InvalidOperationException)
            {
                if (sp1.IsOpen)
                {
                    sp1.Close();
                    LogTerminalInfo(sp1.PortName + " " + "closed");
                }
            }
            catch (System.Exception ex)
            {
                LogTerminalInfo(ex.Message);
            }
        }
        */

        /*
        private void ReadGasPayload(object sender, RoutedEventArgs e)
        {
            PackDecoder decoder = SelectDecoder(packet,decoder_type);

            try
            {
                if (cur_read_start && enable_gas_read)
                {

                    //Then if header read is done, go to read gas payload and packet tails
                    int num_bytes = decoder.PackPayloadLen + decoder.PacketTailLength;
                    LogTerminalInfo("Packet Payload: Bytes to read: " + num_bytes);

                    //Use synchronous read;
                    //Append data to headers.
                    int bytes_read = 0;

                    if (bytes_read < num_bytes)
                    {
                        bytes_read += sp1.Read(packet, decoder.HeaderLength, num_bytes);
                        LogTerminalInfo("Bytes read: " + num_bytes);
                    }

                    decoder.CopyPacket(packet);

                    if (!decoder.IsTailCorrect)
                    {
                        LogTerminalInfo("Packet corrupted. Discarding...");

                        //Clear the rest of buffer;
                        while (sp1.BytesToRead > 0)
                        {
                            byte [] garbage = new byte[packet.Length];
                            sp1.Read(garbage,0,sp1.BytesToRead);
                        }

                        Action action_take = new Action(
                        delegate()
                        {
                            OnLogNextPack(e);

                        });

                        Dispatcher.Invoke(
                            System.Windows.Threading.DispatcherPriority.Normal, 
                            action_take);

                        return;
                    }

                    //Now we assume packet is correct.

                    //Debugging code
                    //-------------------------------------------------------------------------------------------------
                    //-------------------------------------------------------------------------------------------------
                    for (int i = 0; i < decoder.HeaderLength + decoder.PackPayloadLen + decoder.PacketTailLength; i++)
                    {
                        LogTerminalInfo("Byte " + i + ": " + packet[i]);
                    }
                    //-------------------------------------------------------------------------------------------------
                    //-------------------------------------------------------------------------------------------------

                    LogTerminalInfo("Sensor Installed:" + " " + decoder.GetGasNum());

                    try
                    {
                        WriteToTable(sender, packet);
                    }
                    catch (System.NullReferenceException ex)
                    {
                        MessageBox.Show(ex.Message, "ReadGasPayload Exception", MessageBoxButton.OK);
                    }

                    if (_auto_send_server)
                    {

                        Action action_take = new Action(
                        delegate()
                        {
                            OnSendDataToServer(e);
                        });

                        Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);
                        
                    }

                    LogTerminalInfo("Reading process is completed. ");

                    enable_gas_read = false;
                    cur_read_start = false;
                    packet = null;

                    //Fire the event to those who subscribe to begin next log event
                    if (continue_to_log)
                    {
                        //First ACK this packet
                        SendACK(sp1);

#if (DEBUG)
                        LogTerminalInfo("Send ACK");
#endif

                        Action action_take = new Action(
                        delegate()
                        {
                            OnLogNextPack(e);
                            
                        });

                        Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);
                        
                    }
                }
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message, "ReadGasPayload Exception", MessageBoxButton.OK,
                    MessageBoxImage.Error);
                cur_read_start = false;
            }
        }
         */

        /* Split packet into their respective table entries */
        protected void WriteToTable(object sender, byte[] packet)
        {
            /*Create a packet decoder*/
            /* Packet decoder */
            PackDecoder packet_decoder = SelectDecoder(packet, decoder_type);
            /* Create New gas data */
            GasDataUpdateEventArgs gas_params = null;
            ObservableCollection<GasInfo> gas_list;

            try
            {
                gas_list = packet_decoder.GetGasList(gas_name_list,_sensor_family);

                //Using append mode
                try
                {
                    using (StreamWriter gasLogWriter = new StreamWriter(gasLogFileName,true))
                    {
                        foreach (GasInfo sensor in gas_list)
                        {
                            LogTerminalInfo("Sensor type: " + sensor.Gas_name);
                            LogTerminalInfo("Readings: " + String.Format("{0:0.####}", sensor.Cur_val) + " " + "ppm");

                            gasLogWriter.Write(sensor.Cur_val);
                            gasLogWriter.Write(' ');

                            if (AQIStandard.IsChecked)
                            {
                                int aqi = _aqi_calc.GetAQI(sensor.Gas_name, sensor.Cur_val);
                                LogTerminalInfo("AQI reading: " + aqi);
                            }

                        }

                        //Lastly, write a time stamp;
                        gasLogWriter.Write(String.Format("{0:g}", DateTime.Now));
                        gasLogWriter.Write(Environment.NewLine);
                        gasLogWriter.Close();
                    }
                }
                catch (IOException ex)
                {
                    LogTerminalInfo(ex.Message + ". No log is made.");
                    foreach (GasInfo sensor in gas_list)
                    {
                        LogTerminalInfo("Sensor type: " + sensor.Gas_name);
                        LogTerminalInfo("Readings: " + String.Format("{0:0.####}", sensor.Cur_val) + " " + "ppm");
                    }
                }
                
            }
            catch (System.NullReferenceException ex)
            {
                MessageBox.Show(ex.Message, "WriteToTable No packet available",
                    MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            //Update Table Content
            _gas_data_table.UpdateGasTableContent(gas_list);

            gas_params = new GasDataUpdateEventArgs(_gas_data_table);

            //Release gas list resource
            gas_list.Clear();

            if (newDataPresentation != null)
            {
                /* Create a action */
                Action action_take = new Action(
                    delegate()
                    {
                        OnGasDataUpdated(gas_params);
                    });
                Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action_take);
            }
        }
     
        /*
         raises the event by invoking 
        the delegates. The sender is always this, the current instance of 
        the class.
        */   
        protected virtual void OnGasDataUpdated(GasDataUpdateEventArgs gas_params)
        {
            if (GasDataUpdated != null)
            {
                GasDataUpdated(this, gas_params);
            }
        }

        protected virtual void OnLogNextPack(RoutedEventArgs e)
        {
            LogNextPack(this, e);
        }

        protected virtual void OnReadGasPack(RoutedEventArgs e)
        {
            ReadGasPack(this,e);
        }

        protected virtual void OnSendDataToServer(RoutedEventArgs e)
        {
            SendDataToServerEve(this, e);
        }

        private void Debug(string msg)
        {
            Console.WriteLine(msg);
        }

        private void LogTerminalInfo(string msg)
        {
            const int msg_max_length = 1000;

            msg += Environment.NewLine;

            if (COMStatus_text.Text_content.Length < msg_max_length)
            {
                COMStatus_text.Text_content += msg;
            }
            else
            {
                COMStatus_text.Text_content = msg;
            }
        }

        private PackDecoder SelectDecoder(byte [] new_packet, DecoderType new_decoder_type)
        {
            switch (new_decoder_type)
            {
                case DecoderType.BlueTooth:
                    return new BTDecoder(new_packet);

                case DecoderType.MRF:
                default:
                    return new MRFDecoder(new_packet);
            }
        }

        //Send data to the remote server
        private void SendToWebBtn_Click(object sender, RoutedEventArgs e)
        {
            if (_gas_data_table.The_table.Count > 0)
            {
                LogTerminalInfo("Sending data to server...");
                WebPostRequest myPostToWeb = new WebPostRequest("http://pollution.comxa.com/form.php");

                myPostToWeb.Timeout = 10000;

                /*Add Gas information to the web query*/

                AddGasDataToQuery(myPostToWeb);

                AddDateTimeToQuery(myPostToWeb);

                AddGPSToQuery(myPostToWeb);

                try
                {
                    string response = myPostToWeb.GetResponse();
                    //Assume anything after '<' will be discarded
                    int idx = response.IndexOf('<', 0, response.Length);

                    if (idx != -1)
                    {
                        response = response.Substring(0, idx - 1);
                    }
                    LogTerminalInfo("Response from server: " + response);
                }
                catch (TimeoutException ex)
                {
                    System.Console.WriteLine("Webpost send exception: " + ex.Message);
                    LogTerminalInfo("Connection to server timeout, no response");
                }

                myPostToWeb.ClearLocalData();
            }
            else
            {
                MessageBox.Show("No data to be sent", "Send data to web", 
                    MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void AddGasDataToQuery(WebPostRequest webRequest)
        {
            LinkedList<string> gas_name_list = _gas_data_table.GetGasNameList();

            if (gas_name_list.Count > 0)
            {
                foreach (string gas_name in gas_name_list)
                {
                    bool status = true;
                    GasInfo next_gas_info = _gas_data_table.GetGasInfo(gas_name, ref status);

                    if (status == false)
                    {
                        LogTerminalInfo("No gas info available for " + gas_name);
                        continue;
                    }

                    //TODO send packets to the remote database server
                    double ppm = next_gas_info.Cur_val;

                    if (AQIStandard.IsChecked)
                    {
                        int aqi = _aqi_calc.GetAQI(gas_name, ppm);
                        webRequest.Add(gas_name + "_aqi", aqi.ToString());
                    }
                    webRequest.Add(gas_name + "_raw", ppm.ToString());
                    
                }
            }
            else
            {
                LogTerminalInfo("No gas information to be sent");
            }
            

            //Release memory resources
            gas_name_list.Clear();
        }

        private void AddDateTimeToQuery(WebPostRequest webRequest)
        {
            DateTime date_time = NtpClient.GetNetworkTime();

            /*Add Date*/
            String database_value = String.Format("{0:ddMMyyyy}", date_time);
            LogTerminalInfo("Date: " + database_value);
            webRequest.Add("date", database_value);

            /*Add time*/
            database_value = String.Format("{0:HHmm}", date_time);
            LogTerminalInfo("Time: " + database_value);
            webRequest.Add("time", database_value);

        }

        private void AddGPSToQuery(WebPostRequest webRequest)
        {
            webRequest.Add("latitude", _latitude);
            webRequest.Add("longitude", _longitude);
        }

        private void OnSelectGasClick(object sender, RoutedEventArgs e)
        {
            gasSettingWindow = new SettingWindow(gas_name_list);

            gasSettingWindow.Show();
        }

        private void OnAutoSendClick(object sender, RoutedEventArgs e)
        {
            if (AutoSendToServer.IsChecked == true)
            {
                _auto_send_server = true;
            }
            else
            {
                _auto_send_server = false;
            }
        }

        private void OnMRFDecodeClicked(object sender, RoutedEventArgs e)
        {
            if (MRFDecoder.IsChecked == true)
            {
                decoder_type = DecoderType.MRF;
                BluetoothDecoder.IsChecked = false;
            }
            else
            {
                //Force it not possible to manually uncheck
                MRFDecoder.IsChecked = true;
            }

        }

        private void OnBluetoothDecodeClicked(object sender, RoutedEventArgs e)
        {
            if (MRFDecoder.IsChecked == true)
            {
                decoder_type = DecoderType.BlueTooth;
                MRFDecoder.IsChecked = false;
            }
            else
            {
                //Force it not possible to manually uncheck
                BluetoothDecoder.IsChecked = true;
            }
        }

        private void OnMICSFamilyClicked(object sender, RoutedEventArgs e)
        {
            _sensor_family = SensorFamily.MICS;
            MICSFamily.IsChecked = true;
            MQFamily.IsChecked = false;
            MixedFamily.IsChecked = false;
        }

        private void OnMQFamilyClicked(object sender, RoutedEventArgs e)
        {
            _sensor_family = SensorFamily.MQ;
            MICSFamily.IsChecked = false;
            MQFamily.IsChecked = true;
            MixedFamily.IsChecked = false;
        }

        private void OnMixedFamilyClicked(object sender, RoutedEventArgs e)
        {
            //I.e. the mixed combination
            _sensor_family = SensorFamily.MICS_Inv;
            MICSFamily.IsChecked = false;
            MQFamily.IsChecked = false;
            MixedFamily.IsChecked = true;
        }

        private void SendNAK(SerialPort sp)
        {
            sp.Write("N");
        }

        private void SendACK(SerialPort sp)
        {
            sp.Write("Y");
        }

        private void OnAQIStandardClicked(object sender, RoutedEventArgs e)
        {
            if (!AQIStandard.IsChecked)
            {
                _aqi_calc.Reset();
            }
            else
            {
                _aqi_calc.CreateLookupTable();
            }
        }

        private void ArchiveBtn_Click(object sender, RoutedEventArgs e)
        {
            HistoryWindow _archive_data = new HistoryWindow(gasLogFileName);
            _archive_data.Show();
            viewArchiveMenuItem.IsEnabled = false;
            _archive_data.Closed += new EventHandler(EnableArchiveBtn);
        }

        private void EnableArchiveBtn(object sender, EventArgs e)
        {
            viewArchiveMenuItem.IsEnabled = true;
        }

        private void GPSLocationClick(object sender, RoutedEventArgs e)
        {
            GPSLocation editForm = new GPSLocation(ref _longitude, ref _latitude);
            editForm.GPSUpdatedEve += new GPSUpdateHandler(GPSValueHandler);
            editForm.Show();
        }

        private void GPSValueHandler(object sender, GPSUpdatedArgs arg)
        {
            _longitude = arg.Longitude;
            iniConfigTable["longitude"] = String.Format("{0:0.######}", _longitude);
            _latitude = arg.Latitude;
            iniConfigTable["latitude"] = String.Format("{0:0.######}", _latitude);
        }

        private void TestThread_Click(object sender, RoutedEventArgs e)
        {
            Thread my_thread = new Thread(this.TestOpen);
            my_thread.Start();
        }

        private void TestOpen()
        {

            using (StreamWriter gasLogWriter = new StreamWriter(gasLogFileName, true))
            {

            }
        }

        /* Setters and Getters */
        /************************************************************************/
        /**********************************************************************/
        /**********************************************************************/
        /* Data Section */
        private SerialPort sp1;

        /* Whether auto send to server is enabled */
        bool _auto_send_server;

        // add an event of the delegate type
        public event GasDataUpdateHandler GasDataUpdated;
        public event LogNextPacketHandler LogNextPack;
        public event GetPackGasPart ReadGasPack;
        public event SendDataToServer SendDataToServerEve;
        

        /* Gas Table */
        //Dictionary<String, GasInfo> _gas_data_table;
        GasTable _gas_data_table;

        /* Data Present */
        DataPresent newDataPresentation;

        /* Setting Menu */
        SettingWindow gasSettingWindow;

        /* COMStatus text */
        TextBoxContent COMStatus_text;

        /* Window-wide constants */
        const int BUF_SIZE = 64;

        /* Serial port asynchronous section */
        //AsyncCallback sp1_read_head_callback;

        //AsyncCallback sp1_frame_err_callback;

        /*Define which decoder to use*/
        DecoderType decoder_type;

        SensorFamily _sensor_family;

        /* Gas name list */
        List<string> gas_name_list;

        /* Web post stuff */
        //WebPostRequest myPostToWeb;

        AQICalculator _aqi_calc;

        //private FileStream _sensorLogStream;

        //private StreamWriter _log_fs_writer;
        private const string gasLogFileName = ".\\gas.log";
        private const string configFileName = ".\\config.ini";

        private string _longitude;
        private string _latitude;

        Dictionary<string, string> iniConfigTable;

    }

    // add a delegate
    public delegate void GasDataUpdateHandler(object sender, GasDataUpdateEventArgs e);
    public delegate void LogNextPacketHandler(object sender, RoutedEventArgs e);
    public delegate void GetPackGasPart(object sender, RoutedEventArgs e);
    public delegate void SendDataToServer(object sender, RoutedEventArgs e);
}



